/*
 * Copyright (C) 2007 Eskil Bylund
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

using System;
using System.Collections.Generic;
using System.Text;

using Gtk;
using Mono.Unix;

namespace DCSharp.Gui
{
	public class EncodingComboBox : Gtk.ComboBox
	{
		private static int DefaultCodePage = 1252; // Western European
		private static int[] CommonEncodings = new int[] {
			1250, // Central European
			1251, // Cyrillic
			1253  // Greek
		};

		private TreeStore store;
		private bool showEntryDefault;

		public EncodingComboBox(bool showEntryDefault) : base()
		{
			this.showEntryDefault = showEntryDefault;

			RowSeparatorFunc = SeparatorFunc;

			store = new TreeStore(typeof(int), typeof(string));

			// Create the renderer
			CellRendererText renderer = new CellRendererText();
			PackStart(renderer, true);
			SetAttributes(renderer, "text", 1);
			SetCellDataFunc(renderer, CellDataFunc);

			// Populate the model
			List<int> codePages = new List<int>();

			if (showEntryDefault)
			{
				store.AppendValues(-1, Catalog.GetString("Default"));
				store.AppendNode();
			}

			// Add the default encoding
			if (AddEncoding(DefaultCodePage))
			{
				codePages.Add(DefaultCodePage);
				store.AppendNode();
			}

			// Add the common encodings
			bool addSeparator = false;
			foreach (int codePage in CommonEncodings)
			{
				if (AddEncoding(codePage))
				{
					codePages.Add(codePage);
					addSeparator = true;
				}
			}
			if (addSeparator)
			{
				store.AppendNode();
			}

			// Add all the other encodings
			EncodingInfo[] infos = Encoding.GetEncodings();
			infos = Array.FindAll(infos, delegate (EncodingInfo i)
			{
				return TryGetEncoding(i.CodePage) != null;
			});
			Array.Sort(infos, SortFunc);

			TreeIter parent = store.AppendValues(0, Catalog.GetString("Other"));
			foreach (EncodingInfo info in infos)
			{
				Encoding encoding = TryGetEncoding(info.CodePage);
				if (encoding != null && !codePages.Contains(info.CodePage))
				{
					store.AppendValues(parent, info.CodePage, GetName(encoding));
				}
			}

			Model = store;
			Active = 0;

			Show();
		}

		#region Properties

		public Encoding ActiveEncoding
		{
			get
			{
				if (showEntryDefault && Active == 0)
				{
					return null;
				}
				TreeIter iter;
				return GetActiveIter(out iter) ?
					Encoding.GetEncoding((int)store.GetValue(iter, 0)) :
					null;
			}
			set
			{
				store.Foreach(delegate (TreeModel model, TreePath path, TreeIter iter)
				{
					int codePage = (int)model.GetValue(iter, 0);
					if ((value != null && codePage == value.CodePage) ||
						(showEntryDefault && value == null))
					{
						SetActiveIter(iter);
						return true;
					}
					return false;
				});
			}
		}

		protected TreeStore Store
		{
			get { return store; }
		}

		#endregion

		#region Methods

		private int SortFunc(EncodingInfo infoA, EncodingInfo infoB)
		{
			Encoding encA = infoA.GetEncoding();
			Encoding encB = infoB.GetEncoding();

			return String.Compare(encA.EncodingName, encB.EncodingName);
		}

		private bool SeparatorFunc(TreeModel model, TreeIter iter)
		{
			return model.GetValue(iter, 1) == null;
		}

		private void CellDataFunc(CellLayout layout, CellRenderer renderer,
			TreeModel model, TreeIter iter)
		{
			renderer.Sensitive = ((int)model.GetValue(iter, 0)) != 0;
		}

		private bool AddEncoding(int codePage)
		{
			Encoding encoding = TryGetEncoding(codePage);
			if (encoding != null)
			{
				store.AppendValues(encoding.CodePage, GetName(encoding));
			}
			return encoding != null;
		}

		private static string GetName(Encoding encoding)
		{
			// The name is in the form "Name (Type)". Replace the "type" with the web name.
			string name = encoding.EncodingName;

			int i = name.IndexOf(" (");
			if (i > 0)
			{
				name = name.Substring(0, i);
			}
			return String.Format("{0} ({1})", name, encoding.WebName);
		}

		private static Encoding TryGetEncoding(int codePage)
		{
			try
			{
				return Encoding.GetEncoding(codePage);
			}
			catch // (NotSupportedException)
			{
			}
			return null;
		}

		#endregion
	}
}
